ποΈGitΠ―ΡΠ°ποΈ
Node / meshtastic / Meshtastic-Android / files / feature / connections / src / commonMain / kotlin / org / meshtastic / feature / connections / ui / ConnectionsScreen.kt
Displaying Raw β’ Download
feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/ConnectionsScreen.kt renovate/actions-setup-java-5.x (f17e586c) Text, 21.27 KB
T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.connections.ui
Tff7b72import T7ee787androidx.compose.animation.AnimatedContent
Tff7b72import T7ee787androidx.compose.animation.animateContentSize
Tff7b72import T7ee787androidx.compose.animation.fadeIn
Tff7b72import T7ee787androidx.compose.animation.fadeOut
Tff7b72import T7ee787androidx.compose.animation.togetherWith
Tff7b72import T7ee787androidx.compose.foundation.layout.Arrangement
Tff7b72import T7ee787androidx.compose.foundation.layout.Box
Tff7b72import T7ee787androidx.compose.foundation.layout.Column
Tff7b72import T7ee787androidx.compose.foundation.layout.Spacer
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxSize
Tff7b72import T7ee787androidx.compose.foundation.layout.fillMaxWidth
Tff7b72import T7ee787androidx.compose.foundation.layout.height
Tff7b72import T7ee787androidx.compose.foundation.layout.heightIn
Tff7b72import T7ee787androidx.compose.foundation.layout.padding
Tff7b72import T7ee787androidx.compose.foundation.layout.size
Tff7b72import T7ee787androidx.compose.material3.Card
Tff7b72import T7ee787androidx.compose.material3.ExperimentalMaterial3Api
Tff7b72import T7ee787androidx.compose.material3.Icon
Tff7b72import T7ee787androidx.compose.material3.MaterialTheme
Tff7b72import T7ee787androidx.compose.material3.Scaffold
Tff7b72import T7ee787androidx.compose.material3.Text
Tff7b72import T7ee787androidx.compose.runtime.Composable
Tff7b72import T7ee787androidx.compose.runtime.DisposableEffect
Tff7b72import T7ee787androidx.compose.runtime.LaunchedEffect
Tff7b72import T7ee787androidx.compose.runtime.getValue
Tff7b72import T7ee787androidx.compose.runtime.mutableStateOf
Tff7b72import T7ee787androidx.compose.runtime.remember
Tff7b72import T7ee787androidx.compose.runtime.setValue
Tff7b72import T7ee787androidx.compose.ui.Alignment
Tff7b72import T7ee787androidx.compose.ui.Modifier
Tff7b72import T7ee787androidx.compose.ui.text.style.TextAlign
Tff7b72import T7ee787androidx.compose.ui.unit.dp
Tff7b72import T7ee787androidx.lifecycle.compose.collectAsStateWithLifecycle
Tff7b72import T7ee787org.jetbrains.compose.resources.stringResource
Tff7b72import T7ee787org.koin.compose.viewmodel.koinViewModel
Tff7b72import T7ee787org.meshtastic.core.model.ConnectionState
Tff7b72import T7ee787org.meshtastic.core.navigation.Route
Tff7b72import T7ee787org.meshtastic.core.navigation.SettingsRoute
Tff7b72import T7ee787org.meshtastic.core.resources.Res
Tff7b72import T7ee787org.meshtastic.core.resources.connections
Tff7b72import T7ee787org.meshtastic.core.resources.no_device_selected
Tff7b72import T7ee787org.meshtastic.core.resources.set_your_region
Tff7b72import T7ee787org.meshtastic.core.resources.unknown_device
Tff7b72import T7ee787org.meshtastic.core.ui.component.AdaptiveTwoPane
Tff7b72import T7ee787org.meshtastic.core.ui.component.ListItem
Tff7b72import T7ee787org.meshtastic.core.ui.component.MainAppBar
Tff7b72import T7ee787org.meshtastic.core.ui.icon.Language
Tff7b72import T7ee787org.meshtastic.core.ui.icon.MeshtasticIcons
Tff7b72import T7ee787org.meshtastic.core.ui.icon.NoDevice
Tff7b72import T7ee787org.meshtastic.core.ui.util.isLocalNetworkPermissionGranted
Tff7b72import T7ee787org.meshtastic.core.ui.util.rememberRequestLocalNetworkPermission
Tff7b72import T7ee787org.meshtastic.core.ui.viewmodel.ConnectionStatus
Tff7b72import T7ee787org.meshtastic.core.ui.viewmodel.ConnectionsViewModel
Tff7b72import T7ee787org.meshtastic.feature.connections.MOCK_DEVICE_PREFIX
Tff7b72import T7ee787org.meshtastic.feature.connections.NO_DEVICE_SELECTED
Tff7b72import T7ee787org.meshtastic.feature.connections.ScannerViewModel
Tff7b72import T7ee787org.meshtastic.feature.connections.TCP_DEVICE_PREFIX
Tff7b72import T7ee787org.meshtastic.feature.connections.model.DeviceListEntry
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.ConnectingDeviceInfo
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.CurrentlyConnectedInfo
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.DeviceList
Tff7b72import T7ee787org.meshtastic.feature.connections.ui.components.TransportFilterChips
Tff7b72import T7ee787org.meshtastic.feature.settings.navigation.ConfigRoute
Tff7b72import T7ee787org.meshtastic.feature.settings.navigation.getNavRouteFrom
Tff7b72import T7ee787org.meshtastic.feature.settings.radio.RadioConfigViewModel
Tff7b72import T7ee787org.meshtastic.feature.settings.radio.component.PacketResponseStateDialog
Tff7b72import T7ee787kotlin.uuid.ExperimentalUuidApi
T8b949e/**
* Fixed minimum height for the "connected device" card at the top of the Connections screen. Shared across the three UI
* states (NO_DEVICE, CONNECTING, CONNECTED_WITH_NODE) so the card never collapses or jumps size between state
* transitions. Sized to comfortably fit the CONNECTED state (battery/RSSI row + node row + disconnect button).
*/
Tff7b72private Tff7b72val Te6edf3CardMinHeight Tff7b72= T79c0ff1T79c0ff0T79c0ff0.Te6edf3dp
T8b949e/** Composable screen for managing device connections (BLE, TCP, USB). It displays connection status. */
Tf0883e@OptInTb4b4b4(Te6edf3ExperimentalMaterial3ApiTff7b72::Te6edf3classTb4b4b4, Te6edf3ExperimentalUuidApiTff7b72::Te6edf3classTb4b4b4)
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffCyclomaticComplexMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffMagicNumberTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffModifierMissingTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffComposableParamOrderTa5d6ff"Tb4b4b4)
Tf0883e@Composable
Tff7b72fun Td2a8ffConnectionsScreenTb4b4b4(
Te6edf3connectionsViewModelTb4b4b4: Te6edf3ConnectionsViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3scanModelTb4b4b4: Te6edf3ScannerViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3radioConfigViewModelTb4b4b4: Te6edf3RadioConfigViewModel Tff7b72= Te6edf3koinViewModelTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3onClickNodeChipTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onNavigateToNodeDetailsTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onConfigNavigateTb4b4b4: Tb4b4b4(Te6edf3RouteTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3radioConfigState Tff7b72by Te6edf3radioConfigViewModelTb4b4b4.Te6edf3radioConfigStateTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionProgress Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3connectionProgressTextTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionStatus Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3connectionStatusTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectionState Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3connectionStateTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3ourNode Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3ourNodeForDisplayTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3regionUnset Tff7b72by Te6edf3connectionsViewModelTb4b4b4.Te6edf3regionUnsetTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3selectedDevice Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3selectedNotNullFlowTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3persistedDeviceName Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3persistedDeviceNameTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bleDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3bleDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3discoveredTcpDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3discoveredTcpDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3recentTcpDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3recentTcpDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3usbDevices Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3usbDevicesForUiTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3isBleScanning Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3isBleScanningTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3isNetworkScanning Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3isNetworkScanningTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3bleAutoScan Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3bleAutoScanTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3networkAutoScan Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3networkAutoScanTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3showBleTransport Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3showBleTransportTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3showNetworkTransport Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3showNetworkTransportTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3showUsbTransport Tff7b72by Te6edf3scanModelTb4b4b4.Te6edf3showUsbTransportTb4b4b4.Te6edf3collectAsStateWithLifecycleTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3localNetworkPermissionGranted Tff7b72= Te6edf3isLocalNetworkPermissionGrantedTb4b4b4(Tb4b4b4)
T8b949e// Android 17 (API 37) gates NSD/mDNS behind ACCESS_LOCAL_NETWORK. Without this prompt the platform
T8b949e// falls back to the system "Choose a device to connect" picker on every discoverServices() call.
T8b949e// Granting the permission upfront lets discovery run silently in-app.
Tff7b72val Te6edf3requestLocalNetworkPermission Tff7b72=
Te6edf3rememberRequestLocalNetworkPermissionTb4b4b4(
Te6edf3onGranted Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3startNetworkScanTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onDenied Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3stopNetworkScanTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
T8b949e// Auto-start BLE scan on screen entry when the user has previously opted in. Stop on screen exit to save battery.
T8b949e// We use `Unit` as the key so the effect is stable across recompositions β the toggle button manages scan
T8b949e// start/stop directly and must not be interrupted by this effect re-firing when `bleAutoScan` changes.
T8b949e// A LaunchedEffect watches bleAutoScan separately to handle the DataStore delivering `true` after the initial
T8b949e// `false` (disk read latency), without triggering a dispose/restart cycle that kills an in-progress scan.
Te6edf3LaunchedEffectTb4b4b4(Te6edf3bleAutoScanTb4b4b4) Tb4b4b4{ Tff7b72if Tb4b4b4(Te6edf3bleAutoScan Tff7b72&Tff7b72& Tff7b72!Te6edf3scanModelTb4b4b4.Te6edf3isBleScanningTb4b4b4.Te6edf3valueTb4b4b4) Te6edf3scanModelTb4b4b4.Te6edf3startBleScanTb4b4b4(Tb4b4b4) Tb4b4b4}
Te6edf3DisposableEffectTb4b4b4(Tffa657UnitTb4b4b4) Tb4b4b4{ Te6edf3onDispose Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3stopBleScanTb4b4b4(Tb4b4b4) Tb4b4b4} Tb4b4b4}
Te6edf3DisposableEffectTb4b4b4(Te6edf3networkAutoScanTb4b4b4, Te6edf3localNetworkPermissionGrantedTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3networkAutoScan Tff7b72&Tff7b72& Te6edf3localNetworkPermissionGrantedTb4b4b4) Te6edf3scanModelTb4b4b4.Te6edf3startNetworkScanTb4b4b4(Tb4b4b4)
Te6edf3onDispose Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3stopNetworkScanTb4b4b4(Tb4b4b4) Tb4b4b4}
Tb4b4b4}
T8b949e/* Animate waiting for the configurations */
Tff7b72var Te6edf3isWaiting Tff7b72by Te6edf3remember Tb4b4b4{ Te6edf3mutableStateOfTb4b4b4(Tff7b72falseTb4b4b4) Tb4b4b4}
Tff7b72if Tb4b4b4(Te6edf3isWaitingTb4b4b4) Tb4b4b4{
Te6edf3PacketResponseStateDialogTb4b4b4(
Te6edf3state Tff7b72= Te6edf3radioConfigStateTb4b4b4.Te6edf3responseStateTb4b4b4,
Te6edf3onDismiss Tff7b72= Tb4b4b4{
Te6edf3isWaiting Tff7b72= Tff7b72false
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3clearPacketResponseTb4b4b4(Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3onComplete Tff7b72= Tb4b4b4{
Te6edf3getNavRouteFromTb4b4b4(Te6edf3radioConfigStateTb4b4b4.Te6edf3routeTb4b4b4)Tff7b72?.Te6edf3let Tb4b4b4{ Te6edf3route Tff7b72-Tff7b72>
Te6edf3isWaiting Tff7b72= Tff7b72false
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3clearPacketResponseTb4b4b4(Tb4b4b4)
Tff7b72if Tb4b4b4(Te6edf3route Tff7b72=Tff7b72= Te6edf3SettingsRouteTb4b4b4.Te6edf3LoRaTb4b4b4) Tb4b4b4{
Te6edf3onConfigNavigateTb4b4b4(Te6edf3SettingsRouteTb4b4b4.Te6edf3LoRaTb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Te6edf3ScaffoldTb4b4b4(
Te6edf3topBar Tff7b72= Tb4b4b4{
Te6edf3MainAppBarTb4b4b4(
Te6edf3title Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3connectionsTb4b4b4)Tb4b4b4,
Te6edf3ourNode Tff7b72= Te6edf3ourNodeTb4b4b4,
Te6edf3showNodeChip Tff7b72= Te6edf3ourNode Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3ConnectedTb4b4b4,
Te6edf3canNavigateUp Tff7b72= Tff7b72falseTb4b4b4,
Te6edf3onNavigateUp Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3actions Tff7b72= Tb4b4b4{Tb4b4b4}Tb4b4b4,
Te6edf3onClickChip Tff7b72= Tb4b4b4{ Te6edf3onClickNodeChipTb4b4b4(Tffa657itTb4b4b4.Te6edf3numTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3paddingValues Tff7b72-Tff7b72>
Te6edf3BoxTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3ColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxSizeTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3paddingValuesTb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3horizontal Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3verticalArrangement Tff7b72= Te6edf3ArrangementTb4b4b4.Te6edf3spacedByTb4b4b4(T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff4.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3AdaptiveTwoPaneTb4b4b4(
Te6edf3first Tff7b72= Tb4b4b4{
Tff7b72val Te6edf3uiState Tff7b72=
Tff7b72when Tb4b4b4{
Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72&Tff7b72& Te6edf3ourNode Tff7b72!Tff7b72= Tff7b72null Tff7b72-Tff7b72>
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE
Te6edf3connectionState Tff7b72is Te6edf3ConnectionStateTb4b4b4.Te6edf3Connected Tff7b72|Tff7b72|
Te6edf3connectionState Tff7b72=Tff7b72= Te6edf3ConnectionStateTb4b4b4.Te6edf3Connecting Tff7b72|Tff7b72|
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3NO_DEVICE_SELECTED Tff7b72-Tff7b72> Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTING
Tff7b72else Tff7b72-Tff7b72> Te6edf3ConnectionUiStateTb4b4b4.Te6edf3NO_DEVICE
Tb4b4b4}
T8b949e// ββ Connected Device slot ββ
T8b949e// A single Card shell hosts all three states. `animateContentSize` smooths any
T8b949e// height changes, while `heightIn(min = CardMinHeight)` reserves a stable floor so
T8b949e// the card never collapses between states.
Te6edf3CardTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3animateContentSizeTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3AnimatedContentTb4b4b4(
Te6edf3targetState Tff7b72= Te6edf3uiStateTb4b4b4,
Te6edf3label Tff7b72= Ta5d6ff"Ta5d6ffconnection_stateTa5d6ff"Tb4b4b4,
Te6edf3transitionSpec Tff7b72= Tb4b4b4{ Te6edf3fadeInTb4b4b4(Tb4b4b4) Te6edf3togetherWith Te6edf3fadeOutTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4) Tb4b4b4{ Te6edf3state Tff7b72-Tff7b72>
Te6edf3BoxTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3heightInTb4b4b4(Te6edf3min Tff7b72= Te6edf3CardMinHeightTb4b4b4)Tb4b4b4,
Te6edf3contentAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72when Tb4b4b4(Te6edf3stateTb4b4b4) Tb4b4b4{
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE Tff7b72-Tff7b72>
Te6edf3ConnectedDeviceContentTb4b4b4(
Te6edf3ourNode Tff7b72= Te6edf3ourNodeTb4b4b4,
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3onNavigateToNodeDetails Tff7b72= Te6edf3onNavigateToNodeDetailsTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTING Tff7b72-Tff7b72>
Te6edf3ConnectingDeviceContentTb4b4b4(
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3persistedDeviceName Tff7b72= Te6edf3persistedDeviceNameTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3discoveredTcpDevices Tff7b72= Te6edf3discoveredTcpDevicesTb4b4b4,
Te6edf3recentTcpDevices Tff7b72= Te6edf3recentTcpDevicesTb4b4b4,
Te6edf3usbDevices Tff7b72= Te6edf3usbDevicesTb4b4b4,
Te6edf3connectionStatus Tff7b72= Te6edf3connectionStatusTb4b4b4,
Te6edf3connectionProgress Tff7b72= Te6edf3connectionProgressTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3disconnectTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72else Tff7b72-Tff7b72> Te6edf3NoDeviceContentTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e// Region warning sits outside the animated card so it does not affect the
T8b949e// CONNECTED β CONNECTING β NO_DEVICE size transition.
Tff7b72if Tb4b4b4(
Te6edf3uiState Tff7b72=Tff7b72= Te6edf3ConnectionUiStateTb4b4b4.Te6edf3CONNECTED_WITH_NODE Tff7b72&Tff7b72&
Te6edf3regionUnset Tff7b72&Tff7b72&
Te6edf3selectedDevice Tff7b72!Tff7b72= Te6edf3MOCK_DEVICE_PREFIX
Tb4b4b4) Tb4b4b4{
Te6edf3SpacerTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3heightTb4b4b4(T79c0ff8.Te6edf3dpTb4b4b4)Tb4b4b4)
Te6edf3CardTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3ListItemTb4b4b4(
Te6edf3leadingIcon Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3LanguageTb4b4b4,
Te6edf3text Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3set_your_regionTb4b4b4)Tb4b4b4,
Te6edf3onClick Tff7b72= Tb4b4b4{
Te6edf3isWaiting Tff7b72= Tff7b72true
Te6edf3radioConfigViewModelTb4b4b4.Te6edf3setResponseStateLoadingTb4b4b4(Te6edf3ConfigRouteTb4b4b4.Te6edf3LORATb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e// Inclusive transport-visibility filter chips. Sit between the connection card and the
T8b949e// device list so users can hide entire transports they're not using.
Te6edf3TransportFilterChipsTb4b4b4(
Te6edf3showBle Tff7b72= Te6edf3showBleTransportTb4b4b4,
Te6edf3showNetwork Tff7b72= Te6edf3showNetworkTransportTb4b4b4,
Te6edf3showUsb Tff7b72= Te6edf3showUsbTransportTb4b4b4,
Te6edf3onToggleBle Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3setShowBleTransportTb4b4b4(Tff7b72!Te6edf3showBleTransportTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleNetwork Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3setShowNetworkTransportTb4b4b4(Tff7b72!Te6edf3showNetworkTransportTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleUsb Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3setShowUsbTransportTb4b4b4(Tff7b72!Te6edf3showUsbTransportTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3second Tff7b72= Tb4b4b4{
T8b949e// ββ Unified device list ββ
Te6edf3BoxTb4b4b4(Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3weightTb4b4b4(T79c0ff1fTb4b4b4)Tb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3DeviceListTb4b4b4(
Te6edf3connectionState Tff7b72= Te6edf3connectionStateTb4b4b4,
Te6edf3selectedDevice Tff7b72= Te6edf3selectedDeviceTb4b4b4,
Te6edf3bleDevices Tff7b72= Te6edf3bleDevicesTb4b4b4,
Te6edf3usbDevices Tff7b72= Te6edf3usbDevicesTb4b4b4,
Te6edf3discoveredTcpDevices Tff7b72= Te6edf3discoveredTcpDevicesTb4b4b4,
Te6edf3recentTcpDevices Tff7b72= Te6edf3recentTcpDevicesTb4b4b4,
Te6edf3isBleScanning Tff7b72= Te6edf3isBleScanningTb4b4b4,
Te6edf3isNetworkScanning Tff7b72= Te6edf3isNetworkScanningTb4b4b4,
Te6edf3showBleSection Tff7b72= Te6edf3showBleTransportTb4b4b4,
Te6edf3showNetworkSection Tff7b72= Te6edf3showNetworkTransportTb4b4b4,
Te6edf3showUsbSection Tff7b72= Te6edf3showUsbTransportTb4b4b4,
Te6edf3onSelectDevice Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3onSelectedTb4b4b4(Tffa657itTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleBleScan Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3toggleBleScanTb4b4b4(Tb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3onToggleNetworkScan Tff7b72= Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3isNetworkScanning Tff7b72|Tff7b72| Te6edf3localNetworkPermissionGrantedTb4b4b4) Tb4b4b4{
Te6edf3scanModelTb4b4b4.Te6edf3toggleNetworkScanTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
T8b949e// Prefer requesting the runtime grant over letting the platform fall
T8b949e// back to the system NSD picker. Persist the user's intent so that if
T8b949e// they grant after the prompt, the scan starts via the launcher's
T8b949e// onGranted callback and stays on for next session.
Te6edf3scanModelTb4b4b4.Te6edf3persistNetworkAutoScanIntentTb4b4b4(Tff7b72trueTb4b4b4)
Te6edf3requestLocalNetworkPermissionTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Te6edf3onAddManualAddress Tff7b72= Tb4b4b4{ Te6edf3_Tb4b4b4, Te6edf3fullAddress Tff7b72-Tff7b72>
Tff7b72val Te6edf3displayAddress Tff7b72= Te6edf3fullAddressTb4b4b4.Te6edf3removePrefixTb4b4b4(Te6edf3TCP_DEVICE_PREFIXTb4b4b4)
Te6edf3scanModelTb4b4b4.Te6edf3addRecentAddressTb4b4b4(Te6edf3fullAddressTb4b4b4, Te6edf3displayAddressTb4b4b4)
Te6edf3scanModelTb4b4b4.Te6edf3changeDeviceAddressTb4b4b4(Te6edf3fullAddressTb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3onRemoveRecentAddress Tff7b72= Tb4b4b4{ Te6edf3scanModelTb4b4b4.Te6edf3removeRecentAddressTb4b4b4(Tffa657itTb4b4b4.Te6edf3fullAddressTb4b4b4) Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
T8b949e/** Body for the CONNECTED state β sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffConnectedDeviceContentTb4b4b4(
Te6edf3ourNodeTb4b4b4: Te6edf3orgTb4b4b4.Te6edf3meshtasticTb4b4b4.Te6edf3coreTb4b4b4.Te6edf3modelTb4b4b4.Te6edf3NodeTff7b72?Tb4b4b4,
Te6edf3selectedDeviceTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3bleDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3onNavigateToNodeDetailsTb4b4b4: Tb4b4b4(Tffa657IntTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3onClickDisconnectTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3ourNodeTff7b72?.Te6edf3let Tb4b4b4{ Te6edf3node Tff7b72-Tff7b72>
Te6edf3CurrentlyConnectedInfoTb4b4b4(
Te6edf3node Tff7b72= Te6edf3nodeTb4b4b4,
Te6edf3bleDevice Tff7b72= Te6edf3bleDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4} Tff7b72as Te6edf3DeviceListEntryTb4b4b4.Te6edf3BleTff7b72?Tb4b4b4,
Te6edf3onNavigateToNodeDetails Tff7b72= Te6edf3onNavigateToNodeDetailsTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Te6edf3onClickDisconnectTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e/** Body for the CONNECTING state β sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffConnectingDeviceContentTb4b4b4(
Te6edf3selectedDeviceTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3persistedDeviceNameTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3bleDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3discoveredTcpDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3recentTcpDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3usbDevicesTb4b4b4: Te6edf3ListTff7b72<Te6edf3DeviceListEntryTff7b72>Tb4b4b4,
Te6edf3connectionStatusTb4b4b4: Te6edf3ConnectionStatusTb4b4b4,
Te6edf3connectionProgressTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3onClickDisconnectTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3selectedEntry Tff7b72=
Te6edf3bleDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3discoveredTcpDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3recentTcpDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
Tff7b72?: Te6edf3usbDevicesTb4b4b4.Te6edf3find Tb4b4b4{ Tffa657itTb4b4b4.Te6edf3fullAddress Tff7b72=Tff7b72= Te6edf3selectedDevice Tb4b4b4}
T8b949e// Use the entry name if found in scan lists, otherwise fall back to the persisted name
T8b949e// from the last successful selection, and only show "Unknown Device" as a last resort.
Tff7b72val Te6edf3name Tff7b72= Te6edf3selectedEntryTff7b72?.Te6edf3name Tff7b72?: Te6edf3persistedDeviceName Tff7b72?: Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3unknown_deviceTb4b4b4)
Tff7b72val Te6edf3address Tff7b72= Te6edf3selectedEntryTff7b72?.Te6edf3address Tff7b72?: Te6edf3selectedDevice
Te6edf3ConnectingDeviceInfoTb4b4b4(
Te6edf3deviceName Tff7b72= Te6edf3nameTb4b4b4,
Te6edf3deviceAddress Tff7b72= Te6edf3addressTb4b4b4,
Te6edf3connectionStatus Tff7b72= Te6edf3connectionStatusTb4b4b4,
Te6edf3connectionProgress Tff7b72= Te6edf3connectionProgressTb4b4b4,
Te6edf3onClickDisconnect Tff7b72= Te6edf3onClickDisconnectTb4b4b4,
Tb4b4b4)
Tb4b4b4}
T8b949e/** Body for the NO_DEVICE state β sits inside the shared outer Card in [ConnectionsScreen]. */
Tf0883e@Composable
Tff7b72private Tff7b72fun Td2a8ffNoDeviceContentTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3ColumnTb4b4b4(
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3fillMaxWidthTb4b4b4(Tb4b4b4)Tb4b4b4.Te6edf3paddingTb4b4b4(T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3verticalArrangement Tff7b72= Te6edf3ArrangementTb4b4b4.Te6edf3CenterTb4b4b4,
Te6edf3horizontalAlignment Tff7b72= Te6edf3AlignmentTb4b4b4.Te6edf3CenterHorizontallyTb4b4b4,
Tb4b4b4) Tb4b4b4{
Te6edf3IconTb4b4b4(
Te6edf3imageVector Tff7b72= Te6edf3MeshtasticIconsTb4b4b4.Te6edf3NoDeviceTb4b4b4,
Te6edf3contentDescription Tff7b72= Tff7b72nullTb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3sizeTb4b4b4(T79c0ff6T79c0ff4.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3tint Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3outlineVariantTb4b4b4,
Tb4b4b4)
Te6edf3TextTb4b4b4(
Te6edf3text Tff7b72= Te6edf3stringResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3no_device_selectedTb4b4b4)Tb4b4b4,
Te6edf3modifier Tff7b72= Te6edf3ModifierTb4b4b4.Te6edf3paddingTb4b4b4(Te6edf3top Tff7b72= T79c0ff1T79c0ff6.Te6edf3dpTb4b4b4)Tb4b4b4,
Te6edf3style Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3typographyTb4b4b4.Te6edf3bodyLargeTb4b4b4,
Te6edf3textAlign Tff7b72= Te6edf3TextAlignTb4b4b4.Te6edf3CenterTb4b4b4,
Te6edf3color Tff7b72= Te6edf3MaterialThemeTb4b4b4.Te6edf3colorSchemeTb4b4b4.Te6edf3outlineTb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
T8b949e/** Visual state for the connection screen's [AnimatedContent] transition between the three card body variants. */
Tff7b72private Tff7b72enum Tff7b72class T56d364ConnectionUiState Tb4b4b4{
T8b949e/** No device is selected. */
Te6edf3NO_DEVICETb4b4b4,
T8b949e/** A device is selected or we are actively connecting. */
Te6edf3CONNECTINGTb4b4b4,
T8b949e/** Connected with node info available. */
Te6edf3CONNECTED_WITH_NODETb4b4b4,
Tb4b4b4}
Served by rngit 1.5.0 - Generated in 0.13s